package ws.spider.util;

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.StringReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.ArrayList;
import java.util.Hashtable;
import java.util.logging.Logger;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;

import org.w3c.dom.Attr;
import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
import org.xml.sax.SAXParseException;

public class ArmoryQueryEngine 
{
	/**
	 * Player minimum level requirement
	 */
	public final static int LEVEL_CAP = 80;
	
	/**
	 * Retries allowed before generating an error due
	 */
	public final static int NUM_RETRIES = 3;
	
	/**
	 * Logger
	 */
	private Logger logger;
	
	/**
	 * Constructor
	 * Creates a new engine without a logger
	 */
	public ArmoryQueryEngine( )
	{
		
	}
	
	/**
	 * Constructor
	 * Creates a new engine with the specified logger
	 * @param logger Logger for recording errors
	 */
	public ArmoryQueryEngine( Logger logger )
	{
		this.logger = logger;
	}
	
	/**
	 * Gets a guild's info
	 * @param guildName The Guild's name
	 * @return ArrayList of Strings containing the members' names
	 * @throws ArmoryQueryEngineException If any errors occur
	 */
	public ArrayList<String> getGuildInfo( String guildName ) throws ArmoryQueryEngineException
	{
		int retries = 0;
		while( retries < NUM_RETRIES )
		{
			try 
			{
				ArrayList<String> playersList = new ArrayList<String>( );
				String name = guildName.replaceAll( " ", "+" );
				
				Document d = getPage( "http://www.wowarmory.com/guild-info.xml?r=Hakkar&n=" + name + "&p=1" );
				NodeList n = d.getElementsByTagName("character");
					
					
				for ( int k = 0; k < n.getLength(); k++ )
				{
					NamedNodeMap attrs = n.item( k ).getAttributes(); 
					int level = 0;
					String pName = null;
					for( int i = 0; i < attrs.getLength() ; i++ ) 
					{
						Attr attribute = ( Attr ) attrs.item(i);
						if( attribute.getName().equals( "name" ) )
							pName = attribute.getValue();
						else if( attribute.getName().equals( "level" ) )
							level = Integer.parseInt( attribute.getValue() );
					}
					if( LEVEL_CAP == level && pName != null ) 
						playersList.add( pName );
				}
				return playersList;
			}
			catch ( NumberFormatException e ) {
				if( logger != null ) logger.severe( e.getMessage() );
				throw new ArmoryQueryEngineException( ArmoryQueryEngineException.ERR_WRONG_FORMAT );
			}
			catch ( Exception e ) {
				if( logger != null ) logger.warning( "Guild: " + guildName + " Retry: " + retries + " " + e.getMessage() );
				retries++;
			}
		}
		if( logger != null ) logger.severe( "Guild: " + guildName + " " + ArmoryQueryEngineException.ERR_CONNECTION_FAILED );
		throw new ArmoryQueryEngineException( ArmoryQueryEngineException.ERR_CONNECTION_FAILED );
	}
	
	/**
	 * Gets a player's attributes
	 * @param playerName The player's name
	 * @param attributes Desired attributes
	 * @return Hashtable of the desired attributes and their values
	 * @throws ArmoryQueryEngineException If any errors occur
	 */
	public Hashtable<String,String> getPlayerInfo( String playerName, String[] attributes ) throws ArmoryQueryEngineException
	{
		
		int retries = 0;
		while( retries < NUM_RETRIES )
		{
			try 
			{
				Hashtable<String,String> table = new Hashtable<String,String>( );
				
				Document d = getPage( "http://www.wowarmory.com/character-sheet.xml?r=Hakkar&n=" + playerName );
				NodeList n = d.getElementsByTagName("character");
					
				NamedNodeMap attrs = n.item(0).getAttributes(); 
				for (int i = 0; i < attrs.getLength(); i++) 
				{
					Attr attribute = ( Attr )attrs.item(i);
					
					for (int j = 0; j < attributes.length ; j++) 
					{
						if( attribute.getName().equals( attributes[j] ) )
							table.put( attributes[j], attribute.getValue() );
					}
				}
				return table;
			}
			catch ( Exception e ) {
				if( logger != null ) logger.warning( "Player: " + playerName + " Retry: " + retries + " " + e.getMessage() );
				retries++;
			}
		}
		if( logger != null ) logger.severe( "Player: " + playerName + " " + ArmoryQueryEngineException.ERR_CONNECTION_FAILED );
		throw new ArmoryQueryEngineException( ArmoryQueryEngineException.ERR_CONNECTION_FAILED );
	}
	
	/**
	 * Gets the completed achievements by the player of the specified category
	 * Format: <achievID>;<dateCompleted>
	 * @param playerName The player's name
	 * @param category The achievements' category
	 * @return ArrayList of Strings containing the player's achievements
	 * @throws Exception If any errors occur
	 */
	public ArrayList<String> getPlayerAchievs( String playerName, int category ) throws ArmoryQueryEngineException
	{
		int retries = 0;
		while( retries < NUM_RETRIES )
		{
			try 
			{
				ArrayList<String> achievList = new ArrayList<String>( );
				
				Document d = getPage( "http://www.wowarmory.com/character-achievements.xml?r=Hakkar&n=" + playerName + "&c=" + category );
				NodeList n = d.getElementsByTagName( "achievement" );
					
					
				for ( int k = 0; k < n.getLength(); k++ )
				{
					String dateCompleted = null;
					String achievID = null;
					NamedNodeMap attrs = n.item(k).getAttributes(); 
					for (int i = 0; i < attrs.getLength(); i++) 
					{
						Attr attribute = (Attr) attrs.item(i);
						if( attribute.getName().equals("id") )
							achievID = attribute.getValue();
						else if( attribute.getName().equals("dateCompleted") )
							dateCompleted = attribute.getValue();
					}
					if( dateCompleted != null )
						achievList.add( achievID + ";" + dateCompleted );
				}
				return achievList;
			}
			catch ( NumberFormatException e ) {
				if( logger != null ) logger.severe( e.getMessage() );
				throw new ArmoryQueryEngineException( ArmoryQueryEngineException.ERR_WRONG_FORMAT );
			}
			catch ( Exception e ) {
				if( logger != null ) logger.warning( "Player: " + playerName + " Retry: " + retries + " " + e.getMessage() );
				retries++;
			}
		}
		if( logger != null ) logger.severe( "Player: " + playerName + " " + ArmoryQueryEngineException.ERR_CONNECTION_FAILED );
		throw new ArmoryQueryEngineException( ArmoryQueryEngineException.ERR_CONNECTION_FAILED );
	}

	/**
	 * Gets the XML from an armory URL
	 * @param url The URL of the site
	 * @return Document with the contents of the URL
	 * @throws ArmoryQueryEngineException If any errors occur
	 */
	private static Document getPage( String url ) throws ArmoryQueryEngineException
	{
		try {
			DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
	        DocumentBuilder db = dbf.newDocumentBuilder();
	        
	        InputSource is = new InputSource();
	        
			URL loc = new URL( url );
			HttpURLConnection conn = (HttpURLConnection) loc.openConnection();
			String userAgent = "Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.1.2) Gecko/20070219 Firefox/2.0.0.2";
			conn.setRequestProperty( "User-agent", userAgent );
			conn.setConnectTimeout( 800 );
			
			InputStream input = conn.getInputStream();
			BufferedReader reader = new BufferedReader( new InputStreamReader( input ) );
			String line = reader.readLine();
			String buffer = "";

			while (line != null) 
			{
				buffer += line.trim();
				line = reader.readLine();
			}
			
			reader.close();
			is.setCharacterStream(new StringReader( buffer ));
			
			return db.parse( is );
			
		} catch ( SAXParseException e ) {
			throw new ArmoryQueryEngineException( ArmoryQueryEngineException.ERR_WRONG_FORMAT );
		} catch ( Exception e ) {
			throw new ArmoryQueryEngineException( ArmoryQueryEngineException.ERR_CONNECTION_FAILED );
		}
        
	}
}
